using System;
using System.IO;
using System.Net.Sockets;
using System.Net;
using System.Security.Cryptography;

namespace csammisrun.OscarLib.Utility
{
	/// <summary>
	/// Provides static methods for processing messages in SNAC family 0x0017 -- authentication and registration
	/// </summary>
	internal static class SNAC17
	{
    /// <summary>
		/// Sends authorization request -- SNAC(17,02)
		/// </summary>
		/// <param name="dp">A <see cref="DataPacket"/> object containing SNAC(17,07)</param>
		public static void SendAuthorizationRequest(DataPacket dp)
		{
			// Pull apart SNAC(17,07)
			MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
			byte[] SNACData = dp.Data;

			int index = 0;
			ushort keylength = Marshal.ByteArrayToUshort(SNACData, ref index);
			byte[] key = new byte[keylength]; Marshal.CopyArray(SNACData, key, index);
			
			// Construct SNAC(17,02)
			SNACHeader header;
			header.FamilyServiceID = (ushort)SNACFamily.AuthorizationRegistrationService;
			header.FamilySubtypeID = (ushort)AuthorizationRegistrationService.LoginRequest;
			header.Flags = 0;
			header.RequestID = dp.ParentSession.GetNextRequestID();

			OSCARIdentification id = dp.ParentSession.ClientIdentification;
			TLV[] tlvs = new TLV[13];
			tlvs[0] = TLVMarshal.MakeTLV(0x0001, Marshal.StringToByteArray(dp.ParentSession.ScreenName, Marshal.ASCII));
			tlvs[1] = TLVMarshal.MakeTLV(0x0025, dp.ParentSession.HashPassword(key));
			tlvs[2] = TLVMarshal.MakeTLV(0x004C, null);
      tlvs[3] = TLVMarshal.MakeTLV(0x0003, Marshal.StringToByteArray(id.ClientName, Marshal.ASCII));
			tlvs[4] = TLVMarshal.MakeTLV(0x0016, Marshal.UShortToByteArray(id.ClientId));
			tlvs[5] = TLVMarshal.MakeTLV(0x0017, Marshal.UShortToByteArray(id.ClientMajor));
			tlvs[6] = TLVMarshal.MakeTLV(0x0018, Marshal.UShortToByteArray(id.ClientMinor));
			tlvs[7] = TLVMarshal.MakeTLV(0x0019, Marshal.UShortToByteArray(id.ClientLesser));
			tlvs[8] = TLVMarshal.MakeTLV(0x001A, Marshal.UShortToByteArray(id.ClientBuild));
			tlvs[9] = TLVMarshal.MakeTLV(0x0014, Marshal.UIntToByteArray(id.ClientDistribution));
      tlvs[10] = TLVMarshal.MakeTLV(0x000F, Marshal.StringToByteArray("en", Marshal.ASCII));
      tlvs[11] = TLVMarshal.MakeTLV(0x000E, Marshal.StringToByteArray("us", Marshal.ASCII));
			tlvs[12] = TLVMarshal.MakeTLV(0x004A, new byte[] {0x01});
			
			// Send the SNAC
			dp.SNAC = header;
			dp.Data = TLVMarshal.TLVsToByteArray(tlvs);
			SNACFunctions.BuildFLAP(dp);
		}

		/// <summary>
		/// Processes a login response -- SNAC(17,03)
		/// </summary>
		/// <param name="dp">A <see cref="DataPacket"/> object containing SNAC(17,03)</param>
		public static void ProcessLoginResponse(DataPacket dp)
		{
			// Pull apart SNAC(17,03)
			byte[] SNACData = dp.Data;

			int readindex = 0;
			ushort key, keylength, errorcode = 0;
			byte[] cookie = null;
			string screenname, errorurl, BOSaddress, email, pwchangeURL,
				betabuild, betaurl, betainfo, betaname,
				relsbuild, relsurl, relsinfo, relsname,
				betachecksum, relschecksum;

			screenname = errorurl = BOSaddress = email = pwchangeURL =
				betabuild = betaurl = betainfo = betaname =
				relsbuild = relsurl = relsinfo = relsname =
				betachecksum = relschecksum = "";


			while(readindex < SNACData.Length)
			{
				key = Marshal.ByteArrayToUshort(SNACData, ref readindex);
				keylength = Marshal.ByteArrayToUshort(SNACData, ref readindex);
				switch(key)
				{
					case 0x01:	// Screenname
            screenname = Marshal.ByteArrayToString(SNACData, ref readindex, keylength, Marshal.ASCII);
						break;
					case 0x08:	// Authorization failed code
						errorcode = Marshal.ByteArrayToUshort(SNACData, ref readindex);
						break;
					case 0x04:	// Error description URL
            errorurl = Marshal.ByteArrayToString(SNACData, ref readindex, keylength, Marshal.ASCII);
						break;
					case 0x05:	// BOS server address
            BOSaddress = Marshal.ByteArrayToString(SNACData, ref readindex, keylength, Marshal.ASCII);
						break;
					case 0x06:	// Authorization cookie
						cookie = new byte[keylength];
						Marshal.CopyArray(SNACData, cookie, ref readindex);
						break;
					case 0x11:	// Email address string
            email = Marshal.ByteArrayToString(SNACData, ref readindex, keylength, Marshal.ASCII);
						break;
					case 0x40:	// Latest beta build
            betabuild = Marshal.ByteArrayToString(SNACData, ref readindex, keylength, Marshal.ASCII);
						break;
					case 0x41:	// Latest beta URL
            betaurl = Marshal.ByteArrayToString(SNACData, ref readindex, keylength, Marshal.ASCII);
						break;
					case 0x42:	// Latest beta info
            betainfo = Marshal.ByteArrayToString(SNACData, ref readindex, keylength, Marshal.ASCII);
						break;
					case 0x43:	// Latest beta name
            betaname = Marshal.ByteArrayToString(SNACData, ref readindex, keylength, Marshal.ASCII);
						break;
					case 0x44:	// Latest release build
            relsbuild = Marshal.ByteArrayToString(SNACData, ref readindex, keylength, Marshal.ASCII);
						break;
					case 0x45:	// Latest release URL
            relsurl = Marshal.ByteArrayToString(SNACData, ref readindex, keylength, Marshal.ASCII);
						break;
					case 0x46:	// Latest release info
            relsinfo = Marshal.ByteArrayToString(SNACData, ref readindex, keylength, Marshal.ASCII);
						break;
					case 0x47:	// Latest release name
            relsname = Marshal.ByteArrayToString(SNACData, ref readindex, keylength, Marshal.ASCII);
						break;
					case 0x48:	// Latest beta checksum
            betachecksum = Marshal.ByteArrayToString(SNACData, ref readindex, keylength, Marshal.ASCII);
						break;
					case 0x49:	// Latest release checksum
            relschecksum = Marshal.ByteArrayToString(SNACData, ref readindex, keylength, Marshal.ASCII);
						break;
					case 0x54:	// Password change URL
            pwchangeURL = Marshal.ByteArrayToString(SNACData, ref readindex, keylength, Marshal.ASCII);
						break;
					default:
						readindex += keylength;
						break;
				}
			}

			if(errorcode != 0)
			{
				dp.ParentSession.OnLoginFailed(Definitions.GetLoginErrorString(errorcode));
				return;
			}

			// Shut down the authorization connection
			// Socket shutdown is initiated by the server
			dp.ParentSession.OnLoginStatusUpdate("Authorized", 0.33);
			dp.ParentConnection.DisconnectFromServer(false);

			// Create a new connection to the BOS server
			Connection newconn = dp.ParentSession.Connections.CreateNewConnection(0x0001);

			string[] bosinfo = BOSaddress.Split(':');

      newconn.ServerConnnectionCompleted += new ServerConnectionCompletedHandler(newconn_ServerConnnectionCompleted);
      newconn.Server = bosinfo[0];
      newconn.Port = Int32.Parse(bosinfo[1]);
      newconn.Cookie = cookie;
      newconn.ConnectToServer();

			// The connection process continues when the server sends SNAC(01,03)
		}

    // This is only attached to the BOS connection, which needs to be told it's ready for data
    // prior to receiving SNAC(01,02)
    static void newconn_ServerConnnectionCompleted(Connection conn)
    {
      conn.ReadyForData = true;
      conn.ReadHeader();
    }

		/// <summary>
		/// Send MD5 key request -- SNAC(17,06)
		/// </summary>
		/// <param name="sess">A <see cref="Session"/> object</param>
		public static void SendMD5Request(Session sess)
		{
			/*** Send SNAC(17,06) to get the auth key ***/
			SNACHeader sh = new SNACHeader();
			sh.FamilyServiceID = (ushort)SNACFamily.AuthorizationRegistrationService;
			sh.FamilySubtypeID = (ushort)AuthorizationRegistrationService.MD5AuthkeyRequest;
			sh.Flags = 0x0000;
			sh.RequestID = sess.GetNextRequestID();

			TLV[] tlvs = new TLV[3];
      tlvs[0] = TLVMarshal.MakeTLV(0x0001, Marshal.StringToByteArray(sess.ScreenName, Marshal.ASCII));
			tlvs[1] = TLVMarshal.MakeTLV(0x004B, null);
			tlvs[2] = TLVMarshal.MakeTLV(0x005A, null);

			DataPacket dp = Marshal.BuildDataPacket(sess, sh, TLVMarshal.TLVsToByteArray(tlvs));
			SNACFunctions.BuildFLAP(dp);
		}

		/// <summary>
		/// Send SecureID Reply -- SNAC(17,0B)
		/// </summary>
		/// <param name="sess">A <see cref="Session"/> object</param>
		/// <param name="sid">The 6-digit SecureID</param>
		public static void SendSecureID(Session sess, string sid)
		{
			// Construct SNAC(17,0B)
			SNACHeader header = new SNACHeader();
			header.FamilyServiceID = (ushort)SNACFamily.AuthorizationRegistrationService;
			header.FamilySubtypeID = (ushort)AuthorizationRegistrationService.SecureIDResponse;
			header.Flags = 0x0000;
			header.RequestID = sess.GetNextRequestID();

			byte[] buffer = new byte[8];
			buffer[0] = 0; buffer[1] = 6;
      byte[] strarray = Marshal.StringToByteArray(sid, Marshal.ASCII);
			Marshal.CopyArray(strarray, buffer, 0, 2);

			DataPacket dp = Marshal.BuildDataPacket(sess, header, buffer);
			SNACFunctions.BuildFLAP(dp);
		}
	}
}
